package org.yyf.resource.demo.annotation.aop;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yyf.resource.demo.annotation.Log;

import com.alibaba.fastjson.JSON;
import com.anuyun.ms.common.BusinessException;
import com.anuyun.ms.common.Result;
import com.anuyun.ms.util.IdUtil;

@Aspect
public class LogAspect {
	private final Logger log = LoggerFactory.getLogger(this.getClass());

	/**
	 * 记录日志id
	 */
	private String id;
	
	/**
	 * 日志的操作说明
	 */
	private String description;
	
	/**
	 * 是否开启异常处理
	 */
	private boolean handleException; 

	/**
	 * 注解切点
	 */
	@Pointcut("@annotation(com.cn.yyf.annotation.Log)")
	public void point() {
	}

	/**
	 * 前置通知
	 * 
	 * @param joinPoint
	 */
	@Before("point()")
	public void doBefore(JoinPoint joinPoint) {
		this.id = IdUtil.createUUID();
		List<String> parameterTypes= this.getLogAnnotationParm(joinPoint);
		String targetName = joinPoint.getSignature().getDeclaringTypeName();
		String methodName = joinPoint.getSignature().getName();
		
		
		
		String args = JSON.toJSONString(joinPoint.getArgs());
		
		log.info("---------------------------begin--------------------------");
		log.info("当前操作: " + this.description + "---操作ID: " + this.id);
		log.info("调用方法: " + targetName+"."+methodName+"("+parameterTypes.toString()+")");
		log.info("传入参数: " + args);
	}

	/**
	 * 环绕通知
	 * 
	 * @param pJoinPoint
	 * @return
	 */
	@Around("point()")
	public Object serviceAOP(ProceedingJoinPoint pJoinPoint) {

		try {
			return pJoinPoint.proceed();
		} catch (Throwable e) {
			if (this.handleException) {
				if (e instanceof BusinessException) {
					log.info("业务异常: " + e.getMessage());
					return new Result<>(((BusinessException) e).getCode(), e.getMessage());
				} else {
					log.error("系统异常: ", e);
					return new Result<>(1, "系统错误: " + e.getMessage());
				}
			} else {
				throw new RuntimeException(e);
			}
		}

	}

	/**
	 * 返回通知
	 * 
	 * @param joinPoint
	 * @param rvt
	 * @return
	 */
	@AfterReturning(pointcut = "point()", returning = "obj")
	public Object AfterExec(JoinPoint joinPoint, Object obj) {
		log.info("返回参数: " + JSON.toJSONString(obj));
		log.info("操作结束: " + this.description + "---操作ID: " + this.id);
		log.info("---------------------------end----------------------------");
		return obj;
	}

	/**
	 * 异常通知 handleException为false时生效
	 * 
	 * @param joinPoint
	 * @param e
	 */
	@AfterThrowing(pointcut = "point()", throwing = "e")
	public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
		if (e instanceof BusinessException) {
			log.info("业务异常: " + e.getMessage());
		} else {
			log.error("系统异常: ", e);
		}

	}

	/**
	 * 获取注解中对方法的描述信息 用于Controller层注解
	 *
	 */
	@SuppressWarnings("rawtypes")
	public List<String> getLogAnnotationParm(JoinPoint joinPoint) {
		String targetName = joinPoint.getSignature().getDeclaringTypeName();
		String methodName = joinPoint.getSignature().getName();

		Object[] arguments = joinPoint.getArgs();
		Class targetClass = null;
		try {
			targetClass = Class.forName(targetName);
		} catch (ClassNotFoundException e) {
			log.info(targetName + "找不到，无法实例化");
		}
		Method[] methods = targetClass.getMethods();
		Class[] clazzs = null;
		for (Method method : methods) {
			if (method.getName().equals(methodName)) {
				clazzs = method.getParameterTypes();
				if (clazzs.length == arguments.length) {
					this.description = method.getAnnotation(Log.class).value();
					this.handleException = method.getAnnotation(Log.class).handleException();
					break;
				}
			}
		}
		
		List<String> parameterTypes = new ArrayList<>();
		if(clazzs != null) {
			for(Class c :clazzs) {
				parameterTypes.add(c.getSimpleName());
			}
		}

		return parameterTypes;
	}

}
